// @ts-check
// 一个简易的表单校验器，目前仅支持校验简单对象

/**
 * 校验 raw 的元素是否符合规则
 * @param {validator.RawData} raw
 * @param {Array<validator.SingleRule>} allRules
 * @returns {Array<validator.Result>}
 */
export function validate(raw, allRules) {
    if (!raw) return [];
    if (!allRules) return [];

    let result = [];
    for (let i = 0; i < allRules.length; i++) {
        const { id, rules, filter } = allRules[i];

        if (filter && !filter.call(null, raw)) {
            continue;
        } else {
            result.push(validateById(raw, id, rules));
        }
    }

    return result;
}

/**
 * 根据判定结果获取第一个报错信息
 * @param {Array<validator.Result>} results
 * @returns {string}
 */
export function getFirstErrMsg(results) {
    if (!results) return '';

    for (let i = 0; i < results.length; i++) {
        if (!results[i].ok) {
            return results[i].msg || '';
        }
    }

    return '';
}

/**
 * 根据报错信息生成，以 id 为键值的对象
 * @param {Array<validator.Result>} results
 * @returns {Record<string, string>}
 */
export function getErrObj(results) {
    if (!results || results.length <= 0) return {};

    /** @type {Record<string, string>} */
    const obj = {};
    for (let i = 0; i < results.length; i++) {
        const result = results[i];
        if (result.ok === false) {
            obj[result.id] = result.msg || '';
        }
    }
    return obj;
}

/**
 * 根据规则校验一个属性
 * @param {validator.RawData} raw 原始对象
 * @param {string} id
 * @param {Array<validator.Rule>} rules
 * @returns {validator.Result}
 */
function validateById(raw, id, rules) {
    for (let i = 0; i < rules.length; i++) {
        const rule = rules[i];

        // 过滤掉不匹配的规则
        if (rule.filter && !rule.filter.call(null, raw)) continue;

        if (invalidAtRule(rules[i], id, raw)) {
            return { id, ok: false, msg: rule.msg };
        }
    }

    return { id, ok: true };
}

/**
 * 是否在某些条件不达标
 * @param {validator.Rule} rule
 * @param {string} id
 * @param {validator.RawData} raw
 * @returns {boolean}
 */
function invalidAtRule(rule, id, raw) {
    const val = raw[id];

    if (rule.required === true && !exists(val)) {
        return true;
    }

    const valNum = Number(val);
    if (rule.min !== undefined && valNum < rule.min) {
        return true;
    }
    if (rule.max !== undefined && valNum > rule.max) {
        return true;
    }

    if (typeof val === 'string' || Array.isArray(val)) {
        if (rule.minlength && val.length < rule.minlength) return true;
        if (rule.maxlength && val.length > rule.maxlength) return true;
    }

    const valStr = String(val);

    if (rule.phone === true && !isPhone(valStr)) {
        return true;
    }

    if (rule.regexp && !rule.regexp.test(valStr)) return true;
    if (rule.idcard && !isIdcard(valStr)) return true;
    if (rule.func && !rule.func.call(null, val, id, raw)) return true;

    return false;
}

/**
 * 判断是否居民身份证简易版
 * @param {string} val
 * @returns {boolean}
 */
function isIdcard(val) {
    const reg = /^\d{18}|\d{17}x$/i;
    return reg.test(val);
}

/**
 * 判断某个值是否存在
 * @param {any} val
 * @returns {boolean}
 */
function exists(val) {
    return !!val;
}

/**
 * 判断是否手机号
 * @param {string} raw
 * @returns {boolean}
 */
function isPhone(raw) {
    const reg = /^1\d{10}$/;
    return reg.test(raw);
}
